/*
 * Signals.hpp
 *
 *  Created on: Jun 14, 2013
 *      Author: dan
 */

#ifndef SIGNALS_HPP_
#define SIGNALS_HPP_

#include <iostream>
#include <sstream>
#include <map>
using namespace std;

#include <boost/thread.hpp>
using namespace boost;

#include "XMLLogger.hpp"
using namespace logger;


template <class SIG>
class Signal{
#define SYNC boost::mutex::scoped_lock _sync_block(internal_mutex);
public:
	struct ExceptionStopped{};
	class Listener{
	public:
		mutable boost::mutex internal_mutex;
		mutable boost::condition_variable newsig;
		Signal<SIG>& sig;
		std::vector<SIG> signals;
		Listener(Signal<SIG>& sig):sig(sig){
			sig.subscribe(*this);
		}
		~Listener(){
			sig.unsubscribe(*this);
		}
		void push(const SIG& signal){SYNC
			signals.push_back(signal);
			newsig.notify_one();
		}
		bool isEmpty()const{SYNC
			return signals.size();
		}
		bool wait(SIG& s){SYNC
			while(signals.size()==0 && sig.stopped==false){
				newsig.wait(_sync_block);
			}
			if(sig.stopped){ return false; }
			s = signals.front();
			signals.eraise(signals.being());
			return true;
		}
		SIG wait(){SYNC
			while(signals.size()==0 && sig.stopped==false){
				newsig.wait(_sync_block);
			}
			if(sig.stopped){ throw ExceptionStopped(); }
			SIG s = signals.front();
			signals.erase(signals.begin());
			return s;
		}
	};

protected:
	std::vector<Listener*> listeners;
	mutable boost::mutex internal_mutex;
	bool stopped;

	void subscribe(Listener& l){SYNC listeners.push_back(&l);}
	void unsubscribe(Listener& l){SYNC
		for(size_t i=0;i<listeners.size();i++){
			if(&l==(listeners[i])){ listeners.erase(listeners.begin()+i);i=-1; }
		}
	}

public:
	void push(const SIG& sig){ SYNC
		for(size_t i=0;i<listeners.size();i++){
			listeners[i]->push(sig);
		}
	}

#undef SYNC
};






#endif /* SIGNALS_HPP_ */
